package de.hska.swlab.iwwdnw.helper;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import de.hska.swlab.iwwdnw.model.Antwort;
import de.hska.swlab.iwwdnw.model.Frage;
import de.hska.swlab.iwwdnw.model.Kategorie;

/**
 * Hauptfunktion {@link #readFile(String)}.
 */
public final class KategorieReader {
	
	/**
	 * Lese Fragen und Kategorien aus einer Datei.
	 * 
	 * <p>
	 * Zeilen die mit Raute beginnen sind Kommentare.<br>
	 * Datei soll folgendes Format haben:
	 * 
	 * <pre>
	 * Kategorie_ID  Name der Kategorie
	 * ... weitere Kategorien
	 * 
	 * QUESTIONS
	 * 
	 * Kategorie_ID  Fragetext
	 * richtige Antwort
	 * falsche Antwort
	 * falsche Antwort
	 * falsche Antwort
	 * ... wietere Fragen ...
	 * </pre>
	 * 
	 * Die gegeben Kategorie ID muss positiv sein.<br>
	 * In den Texten sind somit keine Zeilenumbrüche möglich. Notfalls HTML benutzen.
	 * <p>
	 * 
	 * @param file
	 *            Pfand zur Datei, welche die Fragen und Kategorien beinhaltet
	 * @return definierte Kategorien mit Fragen
	 * @throws KategorieReaderException
	 *             wenn Datei falsch formatiert ist
	 */
	public static Kategorie[] readFile(String file) throws KategorieReaderException {
		File f = new File(file);
		
		if (!f.canRead()) {
			throw new KategorieReaderException("Datei " + file + " kann nicht gelesen werden");
		}
		
		FileInputStream fis = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		
		try {
			fis = new FileInputStream(f);
			isr = new InputStreamReader(fis);
			br = new BufferedReader(isr);
			
			return readFragen(br);
		} catch (FileNotFoundException e) {
			throw new KategorieReaderException("Datei " + file + " existiert nicht");
		} catch (KategorieReaderException e) {
			// Lesefehler - weiterdelegieren
			throw e;
		} catch (IOException e) {
			throw new KategorieReaderException("Unerwarter Lesefehler in " + file + ":\n"
					+ e.getMessage());
		} finally {
			// versuche Streams auf jeden Fall zu schließen
			try {
				fis.close();
			} catch (Exception e) {
			}
			try {
				isr.close();
			} catch (Exception e) {
			}
			try {
				br.close();
			} catch (Exception e) {
			}
		}
	}
	
	private static Kategorie[] readKategorien(BufferedReader br) throws IOException {
		List<Kategorie> kategorien = new LinkedList<Kategorie>();
		
		// lese Kategorien ein bis es abgebrochen wird
		while (true) {
			String line = br.readLine();
			
			// unerwatetes Ende
			if (line == null) {
				throw new KategorieReaderException("Keine Fragen definiert");
			}
			
			line = line.trim();
			
			// leere Zeilen und Kommentare ignorieren
			if (line.isEmpty() || line.charAt(0) == '#') {
				continue;
			}
			
			// beginne mit Frage - Ende der Kategorien
			if (line.equals("QUESTIONS")) {
				return kategorien.toArray(new Kategorie[0]);
			}
			
			// ermittle letzten Index der Kategrie ID im String
			int numIndex = 0;
			for (; numIndex < line.length(); numIndex++) {
				char c = line.charAt(numIndex);
				
				if (Character.isDigit(c)) {
					// okay, immer noch eine Zahl
				} else if (numIndex == 0) {
					throw new KategorieReaderException("Erwarte eine ID für die Kategorie:\n"
							+ line);
				} else if (c == ' ' || c == '\t') {
					break;
				}
			}
			
			// Kategoriedaten
			int id = Integer.parseInt(line.substring(0, numIndex));
			String text = line.substring(numIndex).trim();
			
			// prüfe ob ID bereits vorkommt
			for (Kategorie kategorie : kategorien) {
				if (kategorie.getId() == id) {
					throw new KategorieReaderException(kategorie.getText()
							+ " belegt bereits die ID " + id + ":\n" + line);
				}
			}
			
			// erstelle Kategorie
			Kategorie kategorie = new Kategorie();
			kategorie.setId(id);
			kategorie.setText(text);
			kategorien.add(kategorie);
		}
	}
	
	private static Kategorie[] readFragen(BufferedReader br) throws IOException {
		Kategorie[] kategorien = readKategorien(br);
		int frageId = 1;
		
		// katgorien gelesen - beginne mit Fragen
		while (true) {
			String line = br.readLine();
			
			if (line == null) {
				return kategorien;
			}
			
			line = line.trim();
			
			if (line.isEmpty() || line.charAt(0) == '#') {
				continue;
			}
			
			// ermittle letzten Index der Kategrie ID im String
			int numIndex = 0;
			for (; numIndex < line.length(); numIndex++) {
				char c = line.charAt(numIndex);
				
				if (Character.isDigit(c)) {
					// okay, immer noch eine Zahl
				} else if (numIndex == 0) {
					throw new KategorieReaderException(
							"Erwarte eine ID für die Kategorie der Frage:\n" + line);
				} else if (c == ' ' || c == '\t') {
					break;
				}
			}
			
			// Fragedaten
			Frage frage = new Frage();
			frage.setText(line.substring(numIndex).trim());
			frage.setId(frageId++);
			
			int id = Integer.parseInt(line.substring(0, numIndex));
			
			boolean kategorieIdValid = false;
			
			// frage zu Kategorie hinzufügen
			for (Kategorie kategorie : kategorien) {
				if (kategorie.getId() == id) {
					kategorieIdValid = true;
					kategorie.addFrage(frage);
					break;
				}
			}
			
			// angebene Kategorie ID nicht vorhanden
			if (!kategorieIdValid) {
				throw new KategorieReaderException("Eine Kategorie mit der ID " + id
						+ " existiert nicht:\n" + line);
			}
			
			List<Antwort> antworten = new ArrayList<Antwort>();
			
			// lese Antworten für fragen
			for (int i = 0; i < 4; i++) {
				String l = br.readLine();
				
				if (l == null) {
					throw new KategorieReaderException(i + ". Antwort für Frage "
							+ frage.getText() + " erwartet:\n");
				}
				
				l = l.trim();
				
				Antwort antwort = new Antwort();
				antwort.setText(l);
				antwort.setRichtig(i == 0);
				antworten.add(antwort);
			}
			
			Collections.shuffle(antworten);
			
			for (int i = 0; i < antworten.size(); i++) {
				antworten.get(i).setId(i + 1);
			}
			
			frage.setAntworten(antworten.toArray(new Antwort[0]));
		}
	}
	
	private KategorieReader() {
		
	}
}
